home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / scvs / scvs.new < prev    next >
Encoding:
Text File  |  1991-11-02  |  53.2 KB  |  2,365 lines

  1. #! /sprite/cmds/perl 
  2. #
  3. #   Scvs is the "Sprite Concurrent Version System", pronounced "skivies".
  4. #   It is a Perl script wrapper for cvs.  See the cvs man page for more
  5. #   details.
  6. #
  7. # $Header: /sprite/src/cmds/scvs/RCS/scvs,v 1.13 91/10/31 13:08:52 jhh Exp Locker: jhh $ SPRITE (Berkeley)
  8. #
  9. # Copyright 1991 Regents of the University of California
  10. # Permission to use, copy, modify, and distribute this
  11. # software and its documentation for any purpose and without
  12. # fee is hereby granted, provided that this copyright
  13. # notice appears in all copies.  The University of California
  14. # makes no representations about the suitability of this
  15. # software for any purpose.  It is provided "as is" without
  16. # express or implied warranty.
  17. #
  18.  
  19. require "option.pl";
  20. require "pwd.pl";
  21. require "ctime.pl";
  22. require "stat.pl";
  23.  
  24. $recurse = 1;
  25. $verbose = 0;
  26. $linkFile = "links";
  27. $debug = 0;
  28. $configFile = "SCVS.config";
  29. $argFile = "args";
  30. $modNameFile = "moduleName";
  31. $userFile = "SCVS/users";
  32.  
  33. @options = (
  34.     $OPT_NIL, $OPT_DOC, $OPT_NIL, 
  35.     "Usage: scvs [scvs options] command [command options]",
  36.     "V", $OPT_TRUE, *verbose, "Verbose",
  37.     "D", $OPT_TRUE, *debug, "Debug",
  38.     "r", $OPT_FUNC, "CvsOpt1", "Check out files read-only",
  39.     "w", $OPT_FUNC, "CvsOpt1", "Check out files read-write (default)",
  40.     "v", $OPT_FUNC, "CvsOpt1", "Print cvs version info",
  41.     "d", $OPT_STRING, *cvsroot, "Specify cvs root directory",
  42.     "e", $OPT_FUNC, "CvsOpt1", "Specify editor to use",
  43.     "H", $OPT_FUNC, "CvsOpt1", "Print help information",
  44. );
  45. undef($cvsargs);
  46. &Opt_Parse(*ARGV, @options, $OPT_OPTIONS_FIRST);
  47. if ($debug) {
  48.     $verbose = 1;
  49. }
  50. $cvsCmdArgs = $cvsargs;
  51.  
  52. @cvsCmds = ("join", "patch", "tag");
  53.  
  54. #
  55. # Global variables.
  56. #
  57. #    %moduleToRepos  maps module name to its relative path within the
  58. #            repository
  59. #    %reposToModule    reverse mapping of moduleToRepos
  60. #    %cwdToMod    maps current working directory to module name
  61. #    %cwdToRoot    maps current working directory within a module copy
  62. #            to the root dir of the module copy
  63. #
  64.  
  65. #
  66. # Config
  67. #
  68. # Find the configuration file and set up various configuration variables.
  69. #
  70. # Results: 0 if successful, 1 otherwise
  71. # Side effects: Some variables are set.
  72. #
  73.  
  74. sub Config {
  75.     local($pwd) = $ENV{'PWD'};
  76.     local($stat, $lastStat) = (0, 0);
  77.     local($tmp);
  78.     local(@attempts);
  79.  
  80.     #
  81.     # Work our way up the directory tree looking for the config file.
  82.     #
  83.     while(! -e $configFile) {
  84.     push(@attempts, $ENV{'PWD'});
  85.     &Chdir("..") == 0 || return 1;
  86.     &Stat(".");
  87.     $stat = $st_dev . $st_ino . $st_serverID;
  88.     last if ($stat eq $lastStat);
  89.     $lastStat = $stat;
  90.     }
  91.     if (! -e $configFile) {
  92.     printf("Couldn't find configuration file\n");
  93.     foreach $tmp (@attempts) {
  94.         printf("Not in $tmp\n");
  95.     }
  96.     return 1;
  97.     }
  98.     open(CONFIG, "$configFile") || die("Can't open $configFile: $!\n");
  99.     while(<CONFIG>) {
  100.     next if (/^\s*#/);
  101.     if (/^cvsroot:\s+(\S+)\s*$/) {
  102.         if (!defined($cvsroot)) {
  103.         $cvsroot = $1;
  104.         }
  105.     } elsif(/^installdir:\s+(\S+)\s*$/) {
  106.         $installdir = $1;
  107.     }
  108.     }
  109.     close(CONFIG);
  110.     if (!defined($cvsroot)) {
  111.     printf("cvsroot not set in config file\n");
  112.     return 1;
  113.     }
  114.     &Chdir("$pwd") == 0 || return 1;
  115.     return 0;
  116. }
  117.  
  118. #
  119. # PackCmd($command, @dirs)
  120. #
  121. # Runs a Pack or Unpack command on each of the directories in the list.
  122. #
  123. # Results: 0 if successful, 1 otherwise
  124. #
  125. # Side effects:  The link file is modified.
  126. #
  127.  
  128. sub PackCmd {
  129.     local($command) = shift;
  130.     local(@dirs) = @_;
  131.     local($status) = 0;
  132.     local($pwd) = $ENV{'PWD'};
  133.  
  134.     if ($#dirs < $[) {
  135.     push(@dirs, '.');
  136.     }
  137.     foreach $dir (@dirs) {
  138.     &Chdir($dir) == 0 || return 1; 
  139.     if ($command eq "pack") {
  140.         $status = &Pack($dir);
  141.     } else {
  142.         $status = &Unpack($dir);
  143.     }
  144.     if ($status) {
  145.         return $status;
  146.     }
  147.     &Chdir($pwd) == 0 || return 1; 
  148.     }
  149. }
  150. #
  151. # Pack($path)
  152. #
  153. # Finds all symbolic links in the current directory and puts them in the
  154. # link file.  The links are stored in alphabetical
  155. # order.  If $recurse is non-zero, Pack will call itself to recurse on
  156. # subdirectories.
  157. #
  158. # Results: 0 if successful, 1 otherwise
  159. #
  160. # Side effects: The link file is modified.
  161. #
  162.  
  163. sub Pack {
  164.     local($path) = shift;
  165.     local($addDir) = 0;
  166.     local($addFile) = 0;
  167.     local(%links);
  168.     local($link);
  169.  
  170.     #
  171.     # Don't pack SCVS subdirectories.
  172.     #
  173.     if ($path =~ m|.*/SCVS|) {
  174.     return 0;
  175.     }
  176.     printf(STDERR "Packing $path\n") if ($debug);
  177.     $addDir = (-d "SCVS") ? 0 : 1;
  178.     $addFile = (-f "SCVS/$linkFile") ? 0 : 1;
  179.     opendir(THISDIR, ".") || return &Error(1, "Opendir of $path failed: $!\n");
  180.     foreach $link (grep(-l, readdir(THISDIR))) {
  181.     printf(STDERR "$link\n") if ($debug);
  182.     $links{$link} = readlink($link);
  183.     }
  184.     close(THISDIR);
  185.     if (defined(%links) || (!$addFile)) {
  186.     if ($addDir) {
  187.         mkdir("SCVS", 0770) ||
  188.         return &Error(1, "Mkdir of SCVS failed: $!\n");
  189.     }
  190.     if (open(PACK, ">SCVS/$linkFile") == 0) {
  191.         printf("Can't open $linkFile: $!\n");
  192.         $status = 1;
  193.         last;
  194.     }
  195.     printf(PACK 
  196.         "# This file is used by scvs and contains symbolic link\n");
  197.     printf(PACK 
  198.         "# information.  Each line is of the form \"link target\"\n");
  199.     printf(PACK "# \$Header\n");
  200.     foreach $link (sort keys %links) {
  201.         printf(PACK "%-24s %s\n", $link, $links{$link});
  202.     }
  203.     close(PACK);
  204.     if ($addFile && (-e "CVS.adm")) {
  205.         if ($addDir) {
  206.         system("cvs -d $cvsroot add SCVS");
  207.         }
  208.         system("cvs -d $cvsroot add -m\"scvs links\" SCVS/$linkFile");
  209.     }
  210.     } 
  211.     if ($recurse) {
  212.     $status = &AllSubdirs($path, "Pack");
  213.     }
  214.     return $status;
  215. }
  216.  
  217. #
  218. # Unpack($path)
  219. #
  220. # Reads the link file in the current directory and creates symbolic links
  221. # from its contents. If recurse is non-zero, Unpack will call itself to 
  222. # recurse on subdirectories.
  223. #
  224. # Results: 0 if successful, 1 otherwise
  225. #
  226. # Side effects: Symbolic links may be created in the current directory
  227. #
  228. sub Unpack {
  229.     local($path) = shift;
  230.     local($status) = 0;
  231.  
  232.     printf(STDERR "Unpacking $path\n") if ($debug);
  233.     if (open(UNPACK, "SCVS/$linkFile")) {
  234.     while(<UNPACK>) {
  235.         next if (/^#/);
  236.         if (/(\S+)\s+(\S+)/) {
  237.         ($link, $value) = ($1, $2);
  238.         if (-l $link) {
  239.             $old = readlink($link);
  240.             if ($old ne $value) {
  241.             printf(
  242.             "Changing $link -> $value, instead of -> $old\n");
  243.             unlink($link);
  244.             } else {
  245.             next;
  246.             }
  247.         } elsif (-e $link) {
  248.             printf("File $link already exists.\n");
  249.             $status = 1;
  250.             next;
  251.         } elsif ($verbose) {
  252.             printf("Creating: $link -> $value\n");
  253.         }
  254.         if (symlink($value, $link) == 0) { 
  255.             printf("Can't create link from $link to $value: $!");
  256.             $status = 1;
  257.         }
  258.         }
  259.     }
  260.     close(UNPACK);
  261.     }
  262.     if ($recurse) {
  263.     $status = &AllSubdirs($path, "Unpack");
  264.     }
  265.     return $status;
  266. }
  267.  
  268. #
  269. # Repository(module)
  270. #
  271. # Finds the pathname of the repository directory for the given module.
  272. #
  273. # Results: The pathname
  274. #
  275. # Side effects: 
  276. #
  277.  
  278. sub Repository {
  279.     local($tmp);
  280.     $tmp = &ReadFile("$_[0]/CVS.adm/Repository", 1);
  281.     if (defined($tmp)) {
  282.     chop($tmp);
  283.     return "$cvsroot/$tmp"; 
  284.     }
  285.     return undef;
  286. }
  287.  
  288. #
  289. # Prune($path)
  290. #
  291. # Removes the given directory if it is empty (no user files or subdirectories).
  292. # Recurses on subdirectories.
  293. #
  294. # Results: 0 if successful, 1 otherwise
  295. #
  296. # Side effects: The directory or its subdirectories may be removed.
  297. #
  298.  
  299. sub Prune {
  300.     local($path) = shift;
  301.     local($i);
  302.     local($status) = 0;
  303.     local($empty) = 1;
  304.     local($tail) = substr($path, rindex($path, '/') + 1);
  305.  
  306.     if ($tail eq "SCVS") {
  307.     return 0;
  308.     }
  309.     print "Pruning $path\n" if ($debug);
  310.     $status = &AllSubdirs($path, "Prune");
  311.     if ($status) {
  312.     return $status;
  313.     }
  314.     opendir(THISDIR, ".") || 
  315.     return &Error(1, "Opendir of $path failed: $!\n"); 
  316.     @contents = grep((-f) || ((!/\./) && ($_ ne 'CVS.adm') && ($_ ne 'SCVS')));
  317.     if ($#contents >= $[
  318.     print "Found @contents in $path\n" if ($debug);
  319.     $empty = 0;
  320.     }
  321.     close(THISDIR);
  322.     if ($empty) {
  323.     print "Prune: chdir to ..\n" if ($debug);
  324.     &Chdir("..") == 0 || return 1;
  325.     print "Prune: deleting $tail\n" if ($debug);
  326.     system("rm -rf $tail");
  327.     }
  328.     return 0;
  329. }
  330.  
  331. #
  332. # CvsOpt1($optString, $nextArg)
  333. #
  334. # Appends $optString to $cvsargs.
  335. #
  336. # Results: 0 
  337. #
  338. # Side effects: None
  339. #
  340. sub CvsOpt1 {
  341.     printf("CvsOpt1 @_\n") if ($debug);
  342.     $cvsargs .= "$_[0] ";
  343.     return 0;
  344. }
  345.  
  346. #
  347. # CvsOpt2($optString, $nextArg)
  348. #
  349. # Appends $optString and $nextArg to $cvsargs.
  350. #
  351. # Results: 1
  352. #
  353. # Side effects: None
  354. #
  355. sub CvsOpt2 {
  356.     printf("CvsOpt2 @_\n") if ($debug);
  357.     $cvsargs .= "$_[0] \"$_[1]\" ";
  358.     return 1;
  359. }
  360.  
  361.  
  362. #
  363. # Checkout(@modules)
  364. #
  365. # Checks out modules.  "cvs co" is used to make a copy of the module. 
  366. # Unpack is used to unpack symbolic links.  
  367. # The current user name is added to the SCVS.users
  368. # file and a list of any other users with a copy of the module are 
  369. # printed.  Any options passed to "cvs co" are stored in the SCVS/args
  370. # file to be used on subsequent updates.
  371. #
  372. # Results: 0 if successful, 1 otherwise
  373. #
  374. # Side effects: A subdirectory is created for each module.
  375. #
  376.  
  377. sub Checkout {
  378.     local(@modules) = @_;
  379.     local($buffer, $i,$repos, $user, $date, %count, %dates);
  380.     local($found, $name);
  381.     local($prune) = 1;
  382.     local($personal) = 0;
  383.     local($args);
  384.     local(@options) = ( 
  385.     "l", $OPT_FALSE, *recurse, "Don't recurse.",
  386.     "P", $OPT_FALSE, *prune, "Don't prune empty directories.",
  387.     "i", $OPT_TRUE, *personal, "Deviation from standard source tree",
  388.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  389.     "c", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  390.     "Q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  391.     "q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  392.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  393.     "n", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  394.     "p", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  395.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  396.     "D", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  397.      );
  398.  
  399.     undef($cvsargs);
  400.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  401.     $args = $cvsargs;
  402.  
  403.     # Put together the "cvs co" command.
  404.  
  405.     $buffer = "cvs -d $cvsroot $cvsCmdArgs co $args";
  406.  
  407.     if ($args =~ /-c/) {
  408.     system("$buffer");
  409.     return 0;
  410.     }
  411.    if (($args =~ /-r/) || ($args =~ /-D/)) {
  412.     $buffer .= "-f ";
  413.     }
  414.     $status = &Lock("r", @modules);
  415.     if ($status) {
  416.     return $status;
  417.     }
  418.     $user = getlogin;
  419.     print "@modules\n" if ($debug);
  420.  
  421. module:
  422.     foreach $i (@modules) {
  423.     local($pwd) = $ENV{'PWD'};
  424.  
  425.     printf("Checking out $i\n") if ($debug);
  426.     # Perform the "cvs co".
  427.  
  428.     printf("$buffer $i \n") if ($debug);
  429.     system("$buffer $i");
  430.  
  431.     # Store the "cvs co" arguments in the info file.
  432.  
  433.     if (! -d "$i/SCVS") {
  434.         if (!mkdir("$i/SCVS", 0770)) {
  435.         $status = &Error(1, "Mkdir of $i/SCVS failed: $!\n");
  436.         next module;
  437.         }
  438.     }
  439.     if (!open(CO, ">$i/SCVS/$argFile")) {
  440.         $status = &Error(1, "Open of $i/SCVS/$argFile failed: $!\n");
  441.         next module;
  442.     }
  443.     print(CO "# This file contains the arguments given when this\n");
  444.     print(CO "# module was checked out.\n");
  445.     print(CO "$cvsCmdArgs\n");
  446.     printf(CO "$args %s\n", $prune ? "-p" : " ");
  447.     close(CO);
  448.  
  449.     &Chdir($i) == 0 || return 1; 
  450.  
  451.     # Unpack the module.
  452.     &Unpack($i) == 0 || return &Error("Unpack of $i failed\n");
  453.  
  454.     # Prune any empty directories in the module.
  455.     if ($prune) {
  456.         &Prune($i) == 0 || return &Error(1, "Prune of $i failed\n");
  457.     }
  458.  
  459.     &Chdir($pwd) == 0 || return 1; 
  460.  
  461.     # See if any other users have a copy of the module, and add our
  462.     # own entry.
  463.  
  464.     $repos = &Repository($i);
  465.     next module if (!defined($repos));
  466.     $date = &ctime(time);
  467.     open(CO2, ">$repos/$tmpfile") ||
  468.         return &Error(1, "Open of $repos/$tmpfile failed: $!\n");
  469.     if (-e "$repos/$userFile") {
  470.         local($copy) = 0;
  471.         open(CO1, "$repos/$userFile") ||
  472.         return &Error(1, "Open of $repos/$userFile failed: $!\n");
  473.         while(<CO1>) {
  474.         $copy = 0;
  475.         next if (/^#/);
  476.         if (/^$user\s+([\w\/\.]+)\s+(.*)/) {
  477.             if ($1 eq "$pwd/$i") {
  478.             $copy = 1;
  479.             } else {
  480.             $found = 1;
  481.             push(@mine, $_);
  482.             }
  483.         } elsif (/^(\S+)\s+([\w\/\.]+)\s+(.*)/) {
  484.             $others{$1} = $3;
  485.         }
  486.         }
  487.         continue {
  488.         if (!$copy) {
  489.             print CO2 $_;
  490.         }
  491.         }
  492.         close(CO1);
  493.     } else {
  494.         printf(CO2 "# List of users with copies of this module.\n");
  495.     }
  496.     if ($#mine >= $[) {
  497.         printf("\nYou also have these copies of the $i module:\n");
  498.         print join("\n", @mine);
  499.     }
  500.     printf(CO2 "$user $pwd/$i %s", &ctime(time));
  501.     close(CO2);
  502.     if (!$personal) {
  503.         if (!rename("$repos/$tmpfile", "$repos/$userFile")) {
  504.         printf(
  505.           "Rename of $repos/$tmpfile to $repos/$userFile failed:$!\n");
  506.         unlink("$repos/$tmpfile");
  507.         next module;
  508.         }
  509.     } else {
  510.         unlink("$repos/$tmpfile");
  511.     }
  512.     if (defined(%others)) {
  513.         printf("\nThe following users have copies of the $i module:\n"); 
  514.         while(($name, $date) = each(%others)) {
  515.         printf("$name $date\n");
  516.         }
  517.     }
  518.     }
  519.     return 0;
  520. }
  521.  
  522. #
  523. # UnlockCmd(@ARGV)
  524. #
  525. # Parse arguements, then call Unlock to do the dirty work. 
  526. #
  527. # Results: 0 if successful, 1 otherwise
  528. #
  529. # Side effects: 
  530. #
  531. sub UnlockCmd {
  532.     local(@args) = @_;
  533.     local($all) = 0;
  534.     local($status) = 0;
  535.     local(@options) = (
  536.     "a", $OPT_TRUE, *all, "Remove everybody's locks",
  537.     );
  538.     &Opt_Parse(*args, @options, $OPT_OPTIONS_FIRST);
  539.     $status = &Unlock($all,@args);
  540.     return $status;
  541. }
  542.  
  543.  
  544. #
  545. # Unlock($allusers, @modules)
  546. #
  547. # Remove the locks for a list of modules.  
  548. #
  549. # Results: 0 if successful, 1 otherwise
  550. #
  551. # Side effects: 
  552. #
  553.  
  554. sub Unlock {
  555.     local($allusers) = shift;
  556.     local(@modules) = @_;
  557.     local($cvsdir, $i, $lock);
  558.     local($status) = 0;
  559.     local($user) = getlogin;
  560.  
  561.     print("Unlock $allusers @modules\n") if ($debug);
  562.     if (!defined(%moduleToRepos)) {
  563.     &ModMap;
  564.     }
  565.     if ($#modules < $[) {
  566.     push(@modules, ".");
  567.     }
  568. module:
  569.     foreach $i (@modules) {
  570.     if ($i eq ".") {
  571.         $i = &GetModuleName;
  572.         if (!defined($i)) {
  573.         $status = 1;
  574.         next module;
  575.         }
  576.     }
  577.     if (!defined($moduleToRepos{$i})) {
  578.         printf(STDERR "Module $i does not exist.\n");
  579.         $status = 1;
  580.         next module;
  581.     }
  582.     $cvsdir = "$cvsroot/$moduleToRepos{$i}/SCVS";
  583.     $lock = "$cvsdir/locks";
  584.     if (!-e $lock) {
  585.         next module;
  586.     }
  587.     if ($allusers) {
  588.         if (!unlink($lock)) {
  589.         printf("Can't remove lock file $lock: $!\n");
  590.         }
  591.         next module;
  592.     }
  593.     if (!open(UNLOCK1, "$lock")) {
  594.         print("Open of $lock failed: $!\n");
  595.         next module;
  596.     }
  597.     if (!open(UNLOCK2, ">$cvsdir/$tmpfile")) {
  598.         print("Open of $cvsdir/$tmpfile failed: $!\n");
  599.         next module;
  600.     }
  601.     flock(UNLOCK1, 2) || 
  602.         return &Error(1, "Flock(2) of $lock failed: $!\n");
  603.  
  604.     while(<UNLOCK1>) {
  605.         ($type, $name) = split(' ');
  606.         if ($name ne $user) {
  607.         print(UNLOCK2 $_);
  608.         }
  609.     }
  610.     close(UNLOCK2);
  611.     if (!rename("$cvsdir/$tmpfile", "$lock")) {
  612.         printf(
  613.           "Rename of $cvsdir/$tmpfile to $lock failed:$!\n");
  614.         unlink("$cvsdir/$tmpfile");
  615.         next module;
  616.     }
  617.     }
  618.     return $status;
  619. }
  620.  
  621. #
  622. # LockCmd(@ARGV)
  623. #
  624. # Parse any options then call Lock to do all the work.
  625. #
  626. # Results: 0 if successful, 1 otherwise
  627. #
  628. # Side effects: The lock files in the modules are updated.
  629. #
  630.  
  631. sub LockCmd {
  632.     local(@args) = @_;
  633.     local($write) = 1;
  634.     local($status) = 0;
  635.     local(@options) = (
  636.     "w", $OPT_TRUE, *write, "Write (exclusive) lock",
  637.     "r", $OPT_FALSE, *write, "Read (shared) lock",
  638.     );
  639.     print("LockCmd @args\n") if ($debug);
  640.     &Opt_Parse(*args, @options, $OPT_OPTIONS_FIRST);
  641.     $status = &Lock($write ? "w" : "r", @args);
  642.     undef(@locks);
  643.     return $status;
  644. }
  645.  
  646.  
  647. #
  648. # Lock($type, @modules)
  649. #
  650. # Make sure the modules are unlocked, and lock them.  Any modules that
  651. # we lock are put in the @lock array.  
  652. #
  653. # Results: 0 if successful, 1 otherwise
  654. #
  655. # Side effects: Lock files are created in the modules.
  656. #
  657.  
  658. sub Lock {
  659.     local($type) = shift;
  660.     local(@dirs) = @_;
  661.     local($cvsdir);
  662.     local($status) = 0;
  663.     local($i, $name);
  664.     local(@mylocks);
  665.     local($user) = getlogin;
  666.     local(@lockFiles);
  667.     local($prevType);
  668.     local($prevName);
  669.     local($prevDate);
  670.     local(@prevLocks);
  671.     local($lock);
  672.     local(@modules);
  673.  
  674.     print("Lock $type @dirs\n") if ($debug);
  675.     if ($#dirs < $[) {
  676.     @dirs = (".");
  677.     }
  678. dir:
  679.     foreach $i (@dirs) {
  680.     #
  681.     # If the directory doesn't exist then assume we've been given
  682.     # a module name instead.
  683.     #
  684.     if (! -d $i) {
  685.         $module = $i;
  686.     } else {
  687.         $module = &GetModuleName;
  688.         if (!defined($module)) {
  689.         printf("Can't find module name for dir $i\n"); 
  690.         $status = 1;
  691.         next dir;
  692.         }
  693.     }
  694.     $repos = $moduleToRepos{$module};
  695.     if (!defined($repos)) {
  696.         printf(STDERR "$i module does not exist.\n");
  697.         $status = 1;
  698.         next dir;
  699.     }
  700.     $cvsdir = "$cvsroot/$repos/SCVS";
  701.     $lock = "$cvsdir/locks";
  702.     print("Cvsdir = $cvsdir\n") if ($debug);
  703.     if (-f "$lock") {
  704.         print("Opening $lock\n") if ($debug);
  705.         open(LOCK1, "$lock") || 
  706.         return &Error(1, "Open of $lock failed: $!\n");
  707.         flock(LOCK1, 2) || 
  708.         return &Error(1, "Flock(2) of $lock failed: $!\n");
  709.         while(<LOCK1>) {
  710.         ($prevType, $prevName) = split(' ');
  711.         if ($prevName eq $user) {
  712.             if ($prevType ne $type) {
  713.             return &Error(1, "$i already locked:\n$_");
  714.             } else {
  715.             close(LOCK1);
  716.             next module;
  717.             }
  718.         } else {
  719.             if (($prevType eq "r") && ($type eq "w")) {
  720.             return &Error(1, "$i already locked:\n$_");
  721.             } elsif ($prevType eq "w") {
  722.             return &Error(1, "$i already locked:\n$_");
  723.             }
  724.         }
  725.         push(@prevLocks, $_);
  726.         }
  727.     }
  728.     open(LOCK2, ">$cvsdir/$tmpfile") ||
  729.         return &Error(1, "Open of $cvsdir/$tmpfile failed: $!\n");
  730.     foreach $i (@prevLocks) {
  731.         print(LOCK2 "$i");
  732.     }
  733.     printf(LOCK2 "$type $user %s", &ctime(time));
  734.     close(LOCK2);
  735.     if (!rename("$cvsdir/$tmpfile", "$lock")) {
  736.         printf(
  737.           "Rename of $cvsdir/$tmpfile to $lock failed:$!\n");
  738.         unlink("$cvsdir/$tmpfile");
  739.         return 1;
  740.     }
  741.     push(@mylocks, $i);
  742.     close(LOCK1);
  743.     }
  744.     if ($status) {
  745.     if (&Unlock(0, @mylocks)) {
  746.         return &Error(1, "Can't clean up in LockCmd\n");
  747.     }
  748.     }
  749.     push(@locks, @mylocks);
  750.     return $status;
  751. }
  752.  
  753. #
  754. # UpdateCmd($lock, @names)
  755. #
  756. # Update modules.  If the arguments are a list of subdirectories then
  757. # we chdir to each of them and run "cvs update".  If the arguments are
  758. # a list of files then we pass them to cvs.  If no files or directories
  759. # are specified then we update the current directory.  The arguments
  760. # for update are retrieved from the SCVS/args file.
  761. #
  762. # Results: 0 if successful, 1 otherwise
  763. #
  764. # Side effects: 
  765. #
  766.  
  767. sub UpdateCmd {
  768.     local($lock) = shift;
  769.     local(@dirs) = @_;
  770.     local(%files);
  771.     local($buffer, $i);
  772.     local($found, $name);
  773.     local($module);
  774.     local($owd);
  775.     local($tmp);
  776.     local($prune);
  777.     local($buildDirs) = 1;
  778.     local($args);
  779.     local($module);
  780.     local(@options) = ( 
  781.     "B", $OPT_FALSE, *buildDirs, "Don't create new directories.",
  782.     "l", $OPT_FALSE, *recurse, "Don't recurse on subdirs",
  783.     "Q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  784.     "q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  785.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  786.     "n", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  787.     "p", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  788.     "d", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  789.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  790.     "D", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  791.     );
  792.     undef($cvsargs);
  793.     &Opt_Parse(*dirs, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  794.     $args = $cvsargs;
  795.  
  796.     print("UpdateCmd in $ENV{'PWD'}\n") if ($debug);
  797.  
  798.     # Put together the "cvs update" command.
  799.  
  800.     if ($buildDirs) {
  801.     $args .= "-d ";
  802.     }
  803.     if (! $recurse) {
  804.     $args .= "-l ";
  805.     }
  806.     $buffer = "cvs -d $cvsroot $cvsCmdArgs ";
  807.  
  808.     if ($#dirs < $[) {
  809.     push(@dirs, ".");
  810.     }
  811.     if (! -d $dirs[0]) {
  812.     $files{"."} = @dirs;
  813.     @dirs = (".");
  814.     }
  815.  
  816.     #
  817.     # Lock the modules.
  818.     #
  819.     if ($lock) {
  820.     $status = &Lock("r", @dirs); 
  821.     if ($status) {
  822.         return $status;
  823.     }
  824.     }
  825.     $owd = $ENV{'PWD'};
  826. dir: 
  827.     foreach $i (@dirs) {
  828.     $prune = 0;
  829.     &Chdir($i) == 0 || return 1; 
  830.     if (-e "SCVS/$argFile") {
  831.         local(@targs);
  832.         @targs = &ReadFile("SCVS/$argFile", 1);
  833.         print("targs = @targs\n") if ($debug);
  834.         if ($targs[1] =~ /(.*)-p(.*)/) {
  835.         $targs[1] = "$1 $2";
  836.         print("Found -p in args file\n") if ($debug);
  837.         $prune = 1;
  838.         }
  839.         chop($targs[0]);
  840.         chop($targs[1]);
  841.     }
  842.     $tmp = "$buffer $targs[0] update $args $targs[1] $files{$i}";
  843.     printf("$tmp\n") if ($debug);
  844.     system($tmp);
  845.     if (&Unpack($i)) {
  846.         printf(STDERR "Unpack of $i failed.\n");
  847.         $status = 1;
  848.     }
  849.     if ($prune) {
  850.         if (&Prune($i)) {
  851.         printf(STDERR "Prune of $i failed.\n");
  852.         $status = 1;
  853.         }
  854.     }
  855.     &Chdir($owd) == 0 || return 1; 
  856.     }
  857. }
  858. return $status;
  859.  
  860.  
  861.  
  862.  
  863.  
  864.  
  865.  
  866.  
  867.  
  868.     if ($lock) {
  869.         $status = &Lock("r","."); 
  870.         if ($status) {
  871.         return $status;
  872.         }
  873.     }
  874.     if (-e "SCVS/$argFile") {
  875.         local(@targs);
  876.         @targs = &ReadFile("SCVS/$argFile", 1);
  877.         print("targs = @targs\n") if ($debug);
  878.         if ($targs[1] =~ /(.*)-p(.*)/) {
  879.         $targs[1] = "$1 $2";
  880.         print("Found -p in args file\n") if ($debug);
  881.         $prune = 1;
  882.         }
  883.         chop($targs[0]);
  884.         chop($targs[1]);
  885.     }
  886.     $tmp = "$buffer $targs[0] update $args $targs[1] @names";
  887.     printf("$tmp\n") if ($debug);
  888.     system($tmp);
  889.     $recurse = 0;
  890.     &Unpack(".") == 0 ||
  891.         return &Error(1, "Unpack of current directory failed.\n");
  892.     } else {
  893.     #
  894.     # Lock the modules.
  895.     #
  896.     if ($lock) {
  897.         $status = &Lock("r", @names); 
  898.         if ($status) {
  899.         return $status;
  900.         }
  901.     }
  902.     $pwd = $ENV{'PWD'};
  903. module: 
  904.     foreach $i (@names) {
  905.         $prune = 0;
  906.         &Chdir($i) == 0 || return 1; 
  907.         if (-e "SCVS/$argFile") {
  908.         local(@targs);
  909.         @targs = &ReadFile("SCVS/$argFile", 1);
  910.         print("targs = @targs\n") if ($debug);
  911.         if ($targs[1] =~ /(.*)-p(.*)/) {
  912.             $targs[1] = "$1 $2";
  913.             print("Found -p in args file\n") if ($debug);
  914.             $prune = 1;
  915.         }
  916.         chop($targs[0]);
  917.         chop($targs[1]);
  918.         }
  919.         $tmp = "$buffer $targs[0] update $args $targs[1]";
  920.         printf("$tmp\n") if ($debug);
  921.         system($tmp);
  922.         if (&Unpack($i)) {
  923.         printf(STDERR "Unpack of $i failed.\n");
  924.         $status = 1;
  925.         }
  926.         if ($prune) {
  927.         if (&Prune($i)) {
  928.             printf(STDERR "Prune of $i failed.\n");
  929.             $status = 1;
  930.         }
  931.         }
  932.  
  933.         &Chdir($pwd) == 0 || return 1; 
  934.     }
  935.     }
  936.     return $status;
  937. }
  938.  
  939. #
  940. # Changed($path)
  941. #
  942. # Use the "cvs info" command to see if the contents of the current directory
  943. # or its subdirectories have been changed by the user.  The modified
  944. # parameter is set to 1 if they have been.
  945. #
  946. # Results: 0 if successful, 1 otherwise; 0 if not modified, 1 otherwise
  947. #
  948. # Side effects: 
  949. #
  950. sub Changed {
  951.     local($path) = shift;
  952.     local($modified) = 0;
  953.     local($status) = 0;
  954.     if (!-d "CVS.adm") {
  955.     return 0;
  956.     }
  957.     open(CHG, "cvs -d $cvsroot info |") ||
  958.     return &Error(1, "Can't do cvs info on $path: $!\n");
  959.     while (<CHG>) {
  960.     if (/^[MC]\s+(\S+)/) {
  961.         printf("$path/$1 has been modified\n");
  962.         $modified = 1;
  963.     } elsif(/^A\s+(\S+)/) {
  964.         printf("$path/$1 has been added\n");
  965.         $modified = 1;
  966.     } elsif(/^R\s+(\S+)/) {
  967.         printf("$path/$1 has been deleted\n");
  968.         $modified = 1;
  969.     }
  970.     }
  971.     close(CHG);
  972.     ($status, @results) = &AllSubdirs($path, "Changed");
  973.     if ($status) {
  974.     return $status;
  975.     }
  976.     while ($#results >= $[) {
  977.     local($substatus) = shift(@results);
  978.     local($submod) = shift(@results);
  979.     if ($substatus) {
  980.         $status = 1;
  981.     }
  982.     if ($submod) {
  983.         $modified = 1;
  984.     }
  985.     }
  986.     return ($status, $modified);
  987. }
  988.  
  989. #
  990. # DoneCmd(@modules)
  991. #
  992. # Process the "done" command.  The user is deleted from the list of users
  993. # for each module.  If the -d flag is specified then the snapshot is
  994. # deleted as well.  If the user has made changes to the snapshot the user
  995. # is warned before the "done" command is completed.
  996. #
  997. # Results: 0 if successful, 1 otherwise
  998. #
  999. # Side effects: 
  1000. #
  1001. sub DoneCmd {
  1002.     local(@modules) = @_;
  1003.     local($status) = 0;
  1004.     local($i);
  1005.     local($me) = getlogin;
  1006.     local($pwd) = $ENV{'PWD'};
  1007.     local($repos, $found);
  1008.     local($delete);
  1009.     local($modified);
  1010.     local(@options) = (
  1011.     "d", $OPT_TRUE, *delete, "Delete module",
  1012.     );
  1013.  
  1014.     $recurse = 1;
  1015.     undef($cvsargs);
  1016.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST); 
  1017.     if ($#modules < $[) {
  1018.     return &Error(1, "Done command requires a list of modules\n");
  1019.     }
  1020.     # Make sure all the modules are unlocked, then lock them.
  1021.     $status = &Lock("r",@modules); 
  1022.     if ($status) {
  1023.     return $status;
  1024.     }
  1025. module:
  1026.     foreach $i (@modules) {
  1027.     $ok = 0;
  1028.     if (! -d $i) {
  1029.         printf("Directory $i not found.\n");
  1030.         next module;
  1031.     }
  1032.     &Chdir($i) == 0 || return 1; 
  1033.     ($status, $modified) = &Changed($i);
  1034.     if ($status) {
  1035.         printf(STDERR "Unable to determine if $i module has changed.\n");
  1036.         $modified = 1;
  1037.     }
  1038.     if ($modified == 1) {
  1039.         printf("Do you wish to continue? [y/n] ");
  1040. prompt:
  1041.         while(1) {
  1042.         $answer = <STDIN>;
  1043.         chop($answer);
  1044.         last prompt if ($answer eq "y");
  1045.         next module if ($answer eq "n");
  1046.         printf("Please answer with \"y\" or \"n\": ");
  1047.         }
  1048.     } elsif ($modified == 1) {
  1049.         next module;
  1050.     }
  1051.  
  1052.     # Update the user file.
  1053.     $repos = &Repository(".");
  1054.     next module if (!defined($repos));
  1055.     if (!open(DONE1, "$repos/$userFile")) {
  1056.         printf("Module $i is not checked out\n");
  1057.         next module;
  1058.     }
  1059.     if (!open(DONE2, ">$repos/$tmpfile")) {
  1060.         printf("Can't open $repos/$tmpfile: $!\n");
  1061.         $status = 1;
  1062.         next module;
  1063.     }
  1064.     $me = getlogin;
  1065.     $found = 0;
  1066.     while (<DONE1>) {
  1067.         if (/^$me\s+([\w\/\.]+)\s+(.*)/) {
  1068.         if ($1 eq "$pwd/$i") {
  1069.             $found = 1;
  1070.             next;
  1071.         }
  1072.         }
  1073.         print DONE2 $_;
  1074.     }
  1075.     close(DONE1);
  1076.     close(DONE2);
  1077.     if (!$found) {
  1078.         printf("Module $i is not checked out\n");
  1079.         next module;
  1080.     }
  1081.     if (!rename("$repos/$tmpfile", "$repos/$userFile")) {
  1082.         printf("Rename of $repos/$tmpfile to $repos/$userFile failed:$!\n");
  1083.         unlink("$repos/$tmpfile");
  1084.         next module;
  1085.     }
  1086.     $ok = 1;
  1087.     }
  1088.     continue {
  1089.     &Chdir($pwd) == 0 || return 1; 
  1090.     if ($ok && $delete) {
  1091.         system("rm -rf $i");
  1092.         if ($?) {
  1093.         printf("Delete of $i failed: $?\n");
  1094.         }
  1095.     }
  1096.     }
  1097.     return $status;
  1098. }
  1099.  
  1100. #
  1101. # AllSubdirs(path, routine, args)
  1102. #
  1103. # Call a routine for each subdirectory of the current directory. The
  1104. # current working directory is changed to the subdirectory before the 
  1105. # routine is called, and the path is modified to reflect this change.
  1106. # The path is passed to the routine when it is called. The routine is
  1107. # called for all subdirectories even if one returns an non-zero status,
  1108. # although this function will then return a non-zero status.
  1109. # Any additional arguments for the routine are passed after the path
  1110. # argument.
  1111. #
  1112. # Results: 0 if successful, 1 if the routine returned non-zero for any
  1113. #         of the subdirectories.
  1114. #
  1115. # Side effects: 
  1116. #
  1117. sub AllSubdirs {
  1118.     local($path) = shift;
  1119.     local($routine) = shift;
  1120.     local($pwd) = $ENV{'PWD'};
  1121.     local($substatus);
  1122.     local($dir);
  1123.     local(@results);
  1124.     local(@status);
  1125.     local(@subdirs);
  1126.  
  1127.     printf(STDERR "AllSubdirs of $routine on $pwd\n") if ($debug);
  1128.     opendir(THISDIR, ".") || 
  1129.     return &Error(1, "Opendir of $path failed: $!\n"); 
  1130.     @subdirs = grep((-d) && (!/^\./) && (! -l) && ($_ ne 'CVS.adm'), 
  1131.             readdir(THISDIR));
  1132.     print("AllSubdirs: @subdirs\n") if ($debug);
  1133.     close(THISDIR);
  1134.     print "@subdirs\n****\n" if ($debug); 
  1135.     foreach $dir (@subdirs) {
  1136.     printf("\t$dir\n") if ($debug);
  1137.     &Chdir($dir) == 0 || return 1; 
  1138.     push(@results, &$routine($path . "/$dir", @_));
  1139.     &Chdir($pwd) == 0 || ($status = 1); 
  1140.     }
  1141.     if (wantarray) {
  1142.     return ($status, @results);
  1143.     }
  1144.     if ($status) {
  1145.     return $status;
  1146.     }
  1147.     @status = grep("$_ != 0", @results);
  1148.     if ($#status >= $[) {
  1149.     return $status[0];
  1150.     }
  1151.     return 0;
  1152. }
  1153.  
  1154.  
  1155. #
  1156. # VerifyCurrent($path, *stale, *modified)
  1157. #
  1158. # Check the status of the files in the current directory and its 
  1159. # subdirectories to see if they are out of date.
  1160. #
  1161. # Results: 0 if successful, 1 otherwise;
  1162. #
  1163. # Side effects: 
  1164. #
  1165. sub VerifyCurrent {
  1166.     local($path) = shift;
  1167.     local(*stale) = shift;
  1168.     local(*modified) = shift;
  1169.     local($pwd) = $ENV{'PWD'};
  1170.     local($status) = 0;
  1171.     local($substatus) = 0;
  1172.     local($current) = 1;
  1173.     local($mod) = 0;
  1174.  
  1175.     printf("Verifying that $path is current\n") if ($debug);
  1176.     if (!-d "CVS.adm") {
  1177.     return 0;
  1178.     }
  1179.     open(CHK, "cvs -d $cvsroot info |") ||
  1180.     return &Error(1, "Can't get info for $path: $!\n");
  1181.     while(<CHK>) {
  1182.     if (/^U\s+(\S+)/) {
  1183.         printf("File $path/$1 is out of date or needs to be added.\n");
  1184.         $current = 0;
  1185.     } elsif (/^D\s+(\S+)/) {
  1186.         printf("File $path/$1 has been removed from the repository.\n");
  1187.         $current = 0;
  1188.     } elsif (/^C\s+(\S+)/) {
  1189.         printf("File $path/$1 is out of date.\n");
  1190.         $current = 0;
  1191.     } elsif (/^[MARC]/) {
  1192.         $mod = 1;
  1193.     } 
  1194.     }
  1195.     close(CHK);
  1196.     if (!$current) {
  1197.     printf("$path is not current\n") if ($debug);
  1198.     push(@stale, $path);
  1199.     }
  1200.     if ($mod) {
  1201.     printf("$path has been modified\n") if ($debug);
  1202.     push(@modified, $path);
  1203.     }
  1204.     if ($recurse) {
  1205.     $status = &AllSubdirs($path, "VerifyCurrent", *stale, *modified);
  1206.     }
  1207.     return $status;
  1208. }
  1209.  
  1210. #
  1211. # UpdateInstalled(@files)
  1212. #
  1213. # Update the installed copy of the sources.  This is done on commit.
  1214. # If @files is not specified then the entire directory and its subdirectories
  1215. # are updated.
  1216. #
  1217. # Results: 0 if successful, 1 otherwise
  1218. #
  1219. # Side effects: The installed sources are updated.
  1220. #
  1221. sub UpdateInstalled {
  1222.     local(@files) = @_;
  1223.     local($dir);
  1224.     local($pwd) = $ENV{'PWD'};
  1225.     local($module);
  1226.  
  1227.     printf(STDERR "UpdateInstalled\n") if ($debug);
  1228.     $module = &GetModuleName;
  1229.     if (!defined($module)) {
  1230.     print("Can't file module name for dir $pwd\n");
  1231.     return 1;
  1232.     }
  1233.     $dir = &ReadFile("$installdir/$module/SCVSCVS.adm/Repository", 1);
  1234.     if (!defined($dir)) {
  1235.     return 1;
  1236.     }
  1237.     chop($dir);
  1238.     &Chdir("$installdir/$dir") == 0 || return 1;
  1239.     &UpdateCmd(0, "-Q", @files) == 0 || return 1;
  1240.     &Chdir("$pwd") == 0 || return 1;
  1241.     return 0;
  1242. }
  1243.  
  1244.  
  1245.  
  1246. #
  1247. # Commit
  1248. #
  1249. # Commit the current directory and its subdirectories.
  1250. #
  1251. # Results: 0 if successful, 1 otherwise
  1252. #
  1253. # Side effects: 
  1254. #
  1255. sub Commit {
  1256.     local($path) = shift;
  1257.     local($args) = shift;
  1258.     local($pwd) = $ENV{'PWD'};
  1259.     local($status) = 0;
  1260.     local($output);
  1261.     local($tail);
  1262.  
  1263.  
  1264.     printf(STDERR "CommitDir $path\n") if ($debug);
  1265.     if (!-d "CVS.adm") {
  1266.     return 0;
  1267.     }
  1268.     printf("$path:\n");
  1269.     $tail = substr($path, rindex($path, '/') + 1);
  1270.     #
  1271.     # Before we commit the SCVS links file we remove all the deleted links
  1272.     # from it.
  1273.     #
  1274.     if ($tail eq "SCVS") {
  1275.     if (open(CMTDIR1, "$linkFile")) {
  1276.         open(CMTDIR2, ">$tmpfile") ||
  1277.         return &Error(1, "Open of $path/$tmpfile failed: $!\n");
  1278.         while(<CMTDIR1>) {
  1279.         next if (/^[*]/);
  1280.         print CMTDIR2 $_;
  1281.         }
  1282.         close(CMTDIR1);
  1283.         close(CMTDIR2);
  1284.         if (!rename("$tmpfile", "$linkFile")) {
  1285.         printf("Rename of $tmpfile to $linkFile failed:$!\n");
  1286.         unlink("$tmpfile");
  1287.         return 1;
  1288.         }
  1289.         system("cvs -d $cvsroot $cvsCmdArgs ci -f -m scvs links");
  1290.     }
  1291.     }
  1292.     system("cvs -d $cvsroot $cvsCmdArgs ci -f -a $args");
  1293.     return $status;
  1294. }
  1295.  
  1296. #
  1297. # CommitCmd(@names)
  1298. #
  1299. # Commit any changes to the modules or files. 
  1300. # Otherwise all changed files in the current directory and any subdirectories
  1301. # are committed.  Before anything is committed it is checked that all
  1302. # files are up-to-date.  If they aren't, a message is printed and the
  1303. # commit is not done.
  1304. #
  1305. # Results: 0 if successful, 1 otherwise
  1306. #
  1307. # Side effects: 
  1308. #
  1309.  
  1310. sub CommitCmd {
  1311.     local(@names) = @_;
  1312.     local($pwd, $i);
  1313.     local($status) = 0;
  1314.     local($path);
  1315.     local(@stale, @modified);
  1316.     local($tmp);
  1317.     local($args);
  1318.     local($quiet) = 0;
  1319.     local(@options) = (
  1320.     "l", $OPT_FALSE, *recurse, "Don't recurse on subdirs",
  1321.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1322.     "n", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1323.     "m", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1324.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1325.     "q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1326.     "Q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1327.     );
  1328.  
  1329.     $recurse = 1;
  1330.     undef($cvsargs);
  1331.     &Opt_Parse(*names, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  1332.     $args = $cvsargs;
  1333.  
  1334.     if ($#names < $[) {
  1335.     push(@names, ".");
  1336.     }
  1337.     if ($args =~ /-q|-Q/) {
  1338.     $quiet = 1;
  1339.     } else {
  1340.     $args .= " -q";
  1341.     }
  1342.     if (! $quiet) {
  1343.     print("Verifying that sources are up-to-date.\n");
  1344.     }
  1345.     if (! -d $names[0]) {
  1346.     $status = &Lock("w","."); 
  1347.     if ($status) {
  1348.         return $status;
  1349.     }
  1350.     $status = &VerifyCurrent(".", *stale, *modified);
  1351.     if ($status) {
  1352.         return $status;
  1353.     }
  1354.     if ($#stale >= $[) {
  1355.         printf("Update your sources using \"scvs update\".\n");
  1356.         return $status;
  1357.     }
  1358.     $tmp = "cvs -d $cvsroot $cvsCmdArgs ci -f $args @names";
  1359.     system($tmp);
  1360.     $status = &UpdateInstalled(@names);
  1361.     } else {
  1362.     $status = &Lock("w",@names); 
  1363.     if ($status) {
  1364.         return $status;
  1365.     }
  1366.     $pwd = $ENV{'PWD'};
  1367.  
  1368.     #
  1369.     # All the modules and their subdirectories must be up-to-date.
  1370.     #
  1371. module:
  1372.     foreach $i (@names) {
  1373.         &Chdir($i) == 0 || return 1; 
  1374.         $status = &VerifyCurrent($i, *stale, *modified);
  1375.         if ($status) {
  1376.         return $status;
  1377.         }
  1378.         &Chdir($pwd) == 0 || return 1; 
  1379.     }
  1380.     
  1381.     if ($#stale >= $[) {
  1382.         printf("Update your sources using \"scvs update\".\n");
  1383.         return $status;
  1384.     }
  1385.     
  1386.     if (! $quiet) {
  1387.         print("Committing modified directories.\n");
  1388.     }
  1389.     #
  1390.     # Commit all directories that were modified.
  1391.     #
  1392.     foreach $i (@modified) {
  1393.         &Chdir($i) == 0 || return 1; 
  1394.         $status = &Commit($i, $args);
  1395.         &Chdir($pwd) == 0 || return 1; 
  1396.     }
  1397.     if (defined($installdir)) {
  1398.         # 
  1399.         # Update the installed copy of the sources.
  1400.         #
  1401.         if (! $quiet) {
  1402.         print("Updating installed copies.\n");
  1403.         }
  1404.         foreach $i (@modified) {
  1405.         &Chdir($i) == 0 || return 1; 
  1406.         $status = &UpdateInstalled;
  1407.         &Chdir($pwd) == 0 || return 1; 
  1408.         }
  1409.     }
  1410.     }
  1411.     return $status;
  1412. }
  1413.  
  1414.  
  1415. #
  1416. # WhoCmd(@modules)
  1417. #
  1418. # Print the names of users who have the modules checked out.
  1419. #
  1420. # Results: 0 if successful, 1 otherwise
  1421. #
  1422. # Side effects: 
  1423. #
  1424.  
  1425. sub WhoCmd {
  1426.     local(@modules) = @_;
  1427.     local($pwd, $i);
  1428.     local($status) = 0;
  1429.     local($cvsdir, @who, $user, %users, $line);
  1430.  
  1431.     if (!defined(%moduleToRepos)) {
  1432.     &ModMap;
  1433.     }
  1434.     if ($#modules < $[) {
  1435.     push(@modules, ".");
  1436.     }
  1437.     $status = &Lock("r",@modules); 
  1438.     if ($status) {
  1439.     return $status;
  1440.     }
  1441.     $pwd = $ENV{'PWD'};
  1442.  
  1443. module:
  1444.     foreach $i (@modules) {
  1445.     if ($i eq ".") {
  1446.         $i = &GetModuleName;
  1447.         if (!defined($i)) {
  1448.         $status = 1;
  1449.         next module;
  1450.         }
  1451.     }
  1452.     if (!defined($moduleToRepos{$i})) {
  1453.         printf(STDERR "$i module does not exist.\n");
  1454.         $status = 1;
  1455.         next module;
  1456.     }
  1457.     $cvsdir = $cvsroot . "/" . $moduleToRepos{$i};
  1458.     @who = &ReadFile("$cvsdir/$userFile", 1);
  1459.     foreach $line (@who) {
  1460.         ($user) = split(' ', $line);
  1461.         $users{$user} = 1;
  1462.     }
  1463.     foreach $user (keys %users) {
  1464.         printf("$user\n");
  1465.     }
  1466.     }
  1467.     return $status;
  1468. }
  1469.  
  1470. #
  1471. # AddCmd(@names)
  1472. #
  1473. # Add a file, directory, or symbolic link to a directory.
  1474. #
  1475. # Results: 0 if successful, 1 otherwise
  1476. #
  1477. # Side effects: 
  1478. #
  1479.  
  1480. sub AddCmd {
  1481.     local(@names) = @_;
  1482.     local($i);
  1483.     local($status) = 0;
  1484.     local(%links);
  1485.     local($pwd) = $ENV{'PWD'};
  1486.     local($module);
  1487.     local($args);
  1488.     local(@options) = (
  1489.     "m", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1490.     );
  1491.  
  1492.     undef($cvsargs);
  1493.     &Opt_Parse(*names, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  1494.     $args = $cvsargs;
  1495.  
  1496.     if ($#names < $[) {
  1497.     return &Error(1, "Add command requires list of files\n");
  1498.     }
  1499.     $module = &GetModuleName;
  1500.     if (!defined($module)) {
  1501.     return 1;
  1502.     }
  1503. name:
  1504.     foreach $i (@names) {
  1505.     if (-l $i) {
  1506.         local($target) = readlink($i);
  1507.         if (!defined($target)) {
  1508.         printf("$i does not exist\n");
  1509.         $status = 1;
  1510.         next name;
  1511.         }
  1512.         if (open(ADD, "SCVS/$linkFile")) {
  1513.         while(<ADD>) {
  1514.             if (/^$i\s+(\S+)/) {
  1515.             if ($target ne $1) {
  1516.                 printf("Link $i already points to $1.\n");
  1517.             } else {
  1518.                 printf("Link $i already added.\n");
  1519.             }
  1520.             $status = 1;
  1521.             close(ADD);
  1522.             next name;
  1523.             }
  1524.         }
  1525.         close(ADD);
  1526.         } elsif (! -f "SCVS/$linkFile") {
  1527.         open(ADD, ">SCVS/$linkFile") ||
  1528.             return &Error(1, "Can't open SCVS/$linkFile: $!\n");
  1529.         printf(ADD 
  1530.         "# This file is used by scvs and contains symbolic link\n");
  1531.         printf(ADD 
  1532.         "# information.  Each line is of the form \"link target\"\n");
  1533.         printf(ADD "# \$Header\n");
  1534.         close(ADD);
  1535.         &Chdir("SCVS") == 0 || return 1; 
  1536.         printf("Adding $linkFile directory\n") if ($debug);
  1537.         system("cvs -d $cvsroot add -m \"sym links\" $linkFile");
  1538.         &Chdir($pwd) == 0 || return 1; 
  1539.         } else {
  1540.         return &Error(1, "Open of SCVS/$linkFile failed: $!\n");
  1541.         }
  1542.         $links{$i} = $target;
  1543.     } else {
  1544.         system("cvs -d $cvsroot $cvsCmdArgs add $args $i");
  1545.         if (-d $i) {
  1546.         # 
  1547.         # If we are adding a directory then we should create an
  1548.         # SCVS subdirectory in it.
  1549.         #
  1550.         if (! -d "$i/SCVS") {
  1551.             mkdir("$i/SCVS", 0770) ||
  1552.             return &Error(1, "Mkdir of $i/SCVS failed: $!\n");
  1553.             &Chdir("$i/SCVS") == 0 || return 1; 
  1554.             open(ADD, ">module") ||
  1555.             return &Error(1, "Open of $i/SCVS/module failed: $!\n");
  1556.             printf(ADD "$module\n");
  1557.             close(ADD);
  1558.             system("cvs -d $cvsroot add module");
  1559.             &Chdir($pwd) == 0 || return 1; 
  1560.         }
  1561.         }
  1562.     }
  1563.     if (defined(%links)) {
  1564.         open(ADD, ">>SCVS/$linkFile") ||
  1565.         return &Error(1, "Open of SCVS/$linkFile failed: $!\n");
  1566.         while (($i, $target) = each(%links)) {
  1567.         printf("Adding link $i -> $target\n") if ($debug);
  1568.         printf(ADD "%-24s %s\n", $i, $target);
  1569.         }
  1570.         close(ADD);
  1571.     }
  1572.     }
  1573.     return $status;
  1574. }
  1575. #
  1576. # RemoveCmd(@names)
  1577. #
  1578. # Removes a file, directory, or symbolic link from a directory.
  1579. #
  1580. # Results: 0 if successful, 1 otherwise
  1581. #
  1582. # Side effects: 
  1583. #
  1584.  
  1585. sub RemoveCmd {
  1586.     local(@names) = @_;
  1587.     local($i);
  1588.     local($status, %links, @delete) = 0;
  1589.  
  1590.     if ($#names < $[) {
  1591.     return &Error(1, "Remove command requires list of files\n");
  1592.     }
  1593.     if (open(RM, "SCVS/$linkFile")) {
  1594.     while(<RM>) {
  1595.         next if (/^#/);
  1596.         if (/^([^*]\S+)\s+(\S+)/) {
  1597.         printf("Found link $1 -> $2\n") if ($debug);
  1598.         $links{$1} = $2;
  1599.         }
  1600.     }
  1601.     close(RM);
  1602.     }
  1603. name:
  1604.     foreach $i (@names) {
  1605.     if (-e $i) {
  1606.         if (-d $i) {
  1607.         print("Ignoring remove of directory $i\n");
  1608.         next name;
  1609.         }
  1610.         printf("Deleting existing $i\n");
  1611.         if (!unlink("$i")) {
  1612.         printf("Unlink failed: $!\n");
  1613.         $status = 1;
  1614.         next name;
  1615.         }
  1616.     }
  1617.     if (defined($links{$i})) {
  1618.         printf("Putting $i on delete list\n") if ($debug);
  1619.         push(@delete, $i);
  1620.     } else {
  1621.         system("cvs -d $cvsroot $cvsCmdArgs remove $i");
  1622.     }
  1623.     }
  1624.     if ($#delete >= $[) {
  1625.     if (!open(RM1, "SCVS/$linkFile")) {
  1626.         printf("Can't open SCVS/$linkFile: $!\n");
  1627.         $status = 1;
  1628.         next name;
  1629.     }
  1630.     if (!open(RM2, ">$tmpfile")) {
  1631.         printf("Can't open $tmpfile: $!\n");
  1632.         $status = 1;
  1633.         next name;
  1634.     }
  1635. line:
  1636.     while (<RM1>) {
  1637.         if (/^([^#*]\S+)\s+(\S+)/) {
  1638.         for ($i = 0; $i <= $#delete; $i++) {
  1639.             if ($delete[$i] eq $1) {
  1640.             splice(@delete, $i, 1);
  1641.             print RM2 "*$_";
  1642.             next line;
  1643.             }
  1644.         }
  1645.         }
  1646.         print RM2 $_;
  1647.     }
  1648.     close(RM1);
  1649.     close(RM2);
  1650.     if (!rename("$tmpfile", "SCVS/$linkFile")) {
  1651.         printf("Rename of $tmpfile to SCVS/$linkFile failed:$!\n");
  1652.         unlink("$tmpfile");
  1653.         $status = 1;
  1654.     }
  1655.     }
  1656.     return $status;
  1657. }
  1658. #
  1659. # Info($path)
  1660. #
  1661. # Prints out status information for the current directory and recurses
  1662. # on subdirectories.
  1663. #
  1664. # Results: 0 if successful, 1 otherwise
  1665. #
  1666. # Side effects: 
  1667. #
  1668. sub Info {
  1669.     local($path) = shift;
  1670.     local($tail);
  1671.     local($diff) = 0;
  1672.     local($cat) = 0;
  1673.     local($i);
  1674.     local($pwd) = $ENV{'PWD'};
  1675.  
  1676.     if (!-d "CVS.adm") {
  1677.     return 0;
  1678.     }
  1679.     print("$path\n");
  1680.     $tail = substr($path, rindex($path, '/') + 1);
  1681.     if ($tail eq "SCVS") {
  1682.     return 0;
  1683.     }
  1684.     system("cvs -d $cvsroot $cvsCmdArgs info ");
  1685.     if (-d "SCVS") {
  1686.     &Chdir("SCVS") == 0 || return 1;
  1687.     open(INFO, "cvs -d $cvsroot $cvsCmdArgs info |") ||
  1688.         return &Error(1, "Can't do cvs info on $path: $!\n");
  1689.     while(<INFO>) {
  1690.         if (/^[UMC]\s+$linkFile/) {
  1691.         $diff = 1;
  1692.         last;
  1693.         } elsif (/^[AD]\s+$linkFile/) {
  1694.         $cat = 1;
  1695.         last;
  1696.         }
  1697.     }
  1698.     close(INFO);
  1699.     if ($diff) {
  1700.         local(%updated);
  1701.         open(INFO, "cvs -d $cvsroot diff $linkFile |") ||
  1702.         return &Error(1, "Can't do cvs diff on $path/$linkFile: $!\n");
  1703.         while(<INFO>) {
  1704.         if (/^>\s+([^*]\S+)/) {
  1705.             printf("A %s\@\n", $1);
  1706.         } elsif (/^>\s+[*](\S+)/) {
  1707.             printf("R %s\@\n", $1);
  1708.             delete $updated{$1};
  1709.         } elsif (/^<\s+([^*]\S+)/) {
  1710.             $updated{$1} = 1;
  1711.         } elsif (/^<\s+[*](\S+)/) {
  1712.             printf("D %s\@\n", $1);
  1713.         }
  1714.         }
  1715.         close(INFO);
  1716.         foreach $i (keys %updated) {
  1717.         printf("U %s\@\n", $i);
  1718.         }
  1719.     }
  1720.     if ($cat) {
  1721.         open(INFO, "$linkFile") ||
  1722.         return &Error(1, "Open of $linkFile failed: $!\n");
  1723.         while(<INFO>) {
  1724.         next if (/^#/);
  1725.         if (/^([^*]\S+)/) {
  1726.             printf("A %s\@\n", $1);
  1727.         } elsif (/^([*]\S+)/) {
  1728.             printf("R %s\@\n", $1);
  1729.         }
  1730.         }
  1731.         close(INFO);
  1732.     }
  1733.     &Chdir($pwd) == 0 || return 1;
  1734.     }
  1735.     if (($recurse) && ($#files < $[)) {
  1736.     $status = &AllSubdirs($path, "Info");
  1737.     }
  1738. }
  1739.  
  1740. #
  1741. # InfoCmd(@modules)
  1742. #
  1743. # Prints out status information for the given modules.
  1744. #
  1745. # Results: 0 if successful, 1 otherwise
  1746. #
  1747. # Side effects: 
  1748. #
  1749.  
  1750. sub InfoCmd {
  1751.     local(@modules) = @_;
  1752.     local($pwd, $i);
  1753.     local($status) = 0;
  1754.     local(@options) = ("l", $OPT_FALSE, *recurse, "Don't recurse on subdirs");
  1755.     local(@targs);
  1756.  
  1757.     $recurse = 1;
  1758.     undef($cvsargs);
  1759.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST);
  1760.     print "@modules\n" if ($debug);
  1761.  
  1762.     if ($#modules < $[) {
  1763.     push(@modules, ".");
  1764.     }
  1765.     if (-e "SCVS/$argFile") {
  1766.     @targs = &ReadFile("SCVS/$argFile", 1);
  1767.     if ($targs[1] =~ /(.*)-p(.*)/) {
  1768.         $targs[1] = "$1 $2";
  1769.     }
  1770.     chop($targs[0]);
  1771.     $cvsCmdArgs .= $targs[0];
  1772.     }
  1773.     if (! -d $modules[0]) {
  1774.     $status = &Lock("r","."); 
  1775.     if ($status) {
  1776.         return $status;
  1777.     }
  1778.     system("cvs -d $cvsroot $cvsCmdArgs info @modules");
  1779.     } else {
  1780.     $status = &Lock("r",@modules);
  1781.     if ($status) {
  1782.         return $status;
  1783.     }
  1784.     $pwd = $ENV{'PWD'};
  1785.     foreach $i (@modules) {
  1786.         printf("InfoCmd %i\n") if ($debug);
  1787.         &Chdir($i) == 0 || return 1; 
  1788.         $status = &Info($i);
  1789.         if ($status) {
  1790.         return $status;
  1791.         }
  1792.         &Chdir($pwd) == 0 || return 1; 
  1793.     }
  1794.     }
  1795.     return $status;
  1796. }
  1797.  
  1798. #
  1799. # DiffFile($path, $file, $args, $current)
  1800. #
  1801. # Prints out status information for the current directory and recurses
  1802. # on subdirectories.
  1803. #
  1804. # Results: 0 if successful, 1 otherwise
  1805. #
  1806. # Side effects: 
  1807. #
  1808. sub DiffFile {
  1809.     local($path) = shift;    # Current path.
  1810.     local($file) = shift;    # File to diff.
  1811.     local($args) = shift;    # args to cvs diff.
  1812.     local($current) = shift;    # Should we diff with current version.
  1813.     local($tail);
  1814.     local($pwd) = $ENV{'PWD'};
  1815.     local($status) = 0;
  1816.     local($version) = "";
  1817.     local($repository);
  1818.  
  1819.     if (!-d "CVS.adm") {
  1820.     return 0;
  1821.     }
  1822.     $repository = &Repository(".");
  1823.     if (!defined($repository)) {
  1824.     print("Repository not found\n") if ($debug);
  1825.     return 0;
  1826.     }
  1827.     printf("Repository is $repository\n") if ($debug);
  1828.     if (!-e "$repository/$file,v") {
  1829.     return 0;
  1830.     }
  1831.     if ($current) {
  1832.     open(DIFF, "cvs -d $cvsroot status $file |") ||
  1833.         return &Error(1, "Can't get status for $path/$file: $!\n");
  1834.     while(<DIFF>) {
  1835.         if (/^RCS:\s+(\S+)/) {
  1836.         $version = "-r $1";
  1837.         last;
  1838.         }
  1839.     }
  1840.     close(DIFF);
  1841.     }
  1842.     system("cvs -d $cvsroot $cvsCmdArgs diff $version $args $file");
  1843. }
  1844.  
  1845. #
  1846. # Diff($path, $args, $current)
  1847. #
  1848. # Prints out status information for the current directory and recurses
  1849. # on subdirectories.
  1850. #
  1851. # Results: 0 if successful, 1 otherwise
  1852. #
  1853. # Side effects: 
  1854. #
  1855. sub Diff {
  1856.     local($path) = shift;    # Current path.
  1857.     local($args) = shift;    # args to cvs diff.
  1858.     local($current) = shift;    # Should we diff with current version.
  1859.     local($tail);
  1860.     local($pwd) = $ENV{'PWD'};
  1861.     local($file);
  1862.     local($status) = 0;
  1863.  
  1864.     if (!-d "CVS.adm") {
  1865.     return 0;
  1866.     }
  1867.     $tail = substr($path, rindex($path, '/') + 1);
  1868.     if ($tail eq "SCVS") {
  1869.     return 0;
  1870.     }
  1871.     opendir(THISDIR, ".") || return &Error(1, "Opendir of $path failed: $!\n");
  1872.     foreach $file (grep(-f, readdir(THISDIR))) {
  1873.     printf(STDERR "$file\n") if ($debug);
  1874.     $status = &DiffFile($path, $file, $args, $current);
  1875.     if ($status) {
  1876.         return $status;
  1877.     }
  1878.     }
  1879.     if ($recurse) {
  1880.     $status = &AllSubdirs($path, "Diff", $args, $current);
  1881.     }
  1882. }
  1883.  
  1884.  
  1885.  
  1886. #
  1887. # DiffCmd(@modules)
  1888. #
  1889. # Does an rcsdiff on the modules or directories
  1890. #
  1891. # Results: 0 if successful, 1 otherwise
  1892. #
  1893. # Side effects: 
  1894. #
  1895.  
  1896. sub DiffCmd {
  1897.     local(@modules) = @_;
  1898.     local($pwd, $i);
  1899.     local($status) = 0;
  1900.     local($current) = 0;
  1901.     local(@options) = (
  1902.     "R", $OPT_TRUE, *current, "Diff with current version",
  1903.     "l", $OPT_FALSE, *recurse, "Recurse on subdirectories",
  1904.     "b", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1905.     "i", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1906.     "w", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1907.     "t", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1908.     "c", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1909.     "e", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1910.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1911.     "h", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1912.     "n", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1913.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1914.     );
  1915.  
  1916.     $recurse = 1;
  1917.     undef($cvsargs);
  1918.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  1919.     print "@modules\n" if ($debug);
  1920.     if ($#modules < $[) {
  1921.     push(@modules, ".");
  1922.     }
  1923.     if (! -d $modules[0]) {
  1924.     $status = &Lock("r","."); 
  1925.     if ($status) {
  1926.         return $status;
  1927.     }
  1928.     foreach $i (@modules) {
  1929.         &DiffFile(".", $i, $cvsargs, $current);
  1930.     }
  1931.     } else {
  1932.     $status = &Lock("r",@modules);
  1933.     if ($status) {
  1934.         return $status;
  1935.     }
  1936.     $pwd = $ENV{'PWD'};
  1937.  
  1938.     foreach $i (@modules) {
  1939.         printf("DiffCmd $i\n") if ($debug);
  1940.         &Chdir($i) == 0 || return 1; 
  1941.         $status = &Diff($i, $cvsargs, $current);
  1942.         if ($status) {
  1943.         return $status;
  1944.         }
  1945.         &Chdir($pwd) == 0 || return 1; 
  1946.     }
  1947.     }
  1948.     return $status;
  1949. }
  1950.  
  1951. #
  1952. # Cvs($path, $command)
  1953. #
  1954. # Run a cvs command in the current directory and its subdirectories.
  1955. # Any output from the command is printed.  The command is not executed
  1956. # in any "SCVS" subdirectories.
  1957. #
  1958. # Results: 0 if successful, 1 otherwise
  1959. #
  1960. # Side effects: 
  1961. #
  1962. sub Cvs {
  1963.     local($path) = shift;
  1964.     local($command) = shift;
  1965.     local($pwd) = $ENV{'PWD'};
  1966.     local($status) = 0;
  1967.     local($output, $tail);
  1968.  
  1969.     if (!-d "CVS.adm") {
  1970.     return 0;
  1971.     }
  1972.     $tail = substr($path, rindex($path, '/') + 1);
  1973.     if ($tail eq "SCVS") {
  1974.     return 0;
  1975.     }
  1976.     printf("%s\n", $path);
  1977.     system("cvs -d $cvsroot $cvsCmdArgs $command");
  1978.     if ($recurse) {
  1979.     $status = &AllSubdirs($path, "Cvs", $command);
  1980.     }
  1981.     return $status;
  1982. }
  1983.  
  1984.  
  1985. #
  1986. # CvsCmd($command, @modules)
  1987. #
  1988. # Runs a cvs command on each module and its subdirectories.
  1989. # Any output from the command is printed.
  1990. #
  1991. # Results: 0 if successful, 1 otherwise
  1992. #
  1993. # Side effects: 
  1994. #
  1995.  
  1996. sub CvsCmd {
  1997.     local($command) = shift;
  1998.     local(@modules) = @_;
  1999.     local($i, @args);
  2000.     local($status) = 0;
  2001.     local($path);
  2002.     local($pwd) = $ENV{'PWD'};
  2003.     local(@options) = (
  2004.     "l", $OPT_FALSE, *recurse, "Don't recurse on subdirs",
  2005.     "L", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  2006.     "R", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  2007.     "h", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  2008.     "t", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  2009.     "b", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  2010.     "d", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  2011.     "l", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  2012.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  2013.     "s", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  2014.     "w", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  2015.     );
  2016.  
  2017.  
  2018.     $recurse = 1;
  2019.     undef($cvsargs);
  2020.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  2021.  
  2022.     if ($#modules < $[) {
  2023.     push(@modules, ".");
  2024.     }
  2025.     if (! -d $modules[0]) {
  2026.     $status = &Lock("r","."); 
  2027.     if ($status) {
  2028.         return $status;
  2029.     }
  2030.     $tmp = "cvs -d $cvsroot $cvsCmdArgs $command $cvsargs @modules";
  2031.     print "$tmp\n" if ($debug);
  2032.     system($tmp);
  2033.     } else {
  2034.     $status = &Lock("r", @modules); 
  2035.     if ($status) {
  2036.         return $status;
  2037.     }
  2038. module: 
  2039.     foreach $i (@modules) {
  2040.         &Chdir($i) == 0 || return 1; 
  2041.         $status = &Cvs($i, $command);
  2042.         &Chdir($pwd) == 0 || return 1; 
  2043.     }
  2044.     }
  2045.     return $status;
  2046. }
  2047.  
  2048.  
  2049.  
  2050. #
  2051. # Exit
  2052. #
  2053. # Exit with a status of 1.
  2054. #
  2055. # Results: Doesn't return
  2056. #
  2057. # Side effects: The script exits.
  2058. #
  2059.  
  2060.  
  2061. sub Exit {
  2062.     exit(1);
  2063. }
  2064.  
  2065.  
  2066. #
  2067. # Usage(@optionArray)
  2068. #
  2069. # Print out help information.
  2070. #
  2071. # Results: None
  2072. #
  2073. # Side effects: Stuff is printed
  2074. #
  2075. sub Usage {
  2076.     local(@options) = @_;
  2077.     local(%info) = (("unpack", "Create symbolic links"),
  2078.             ("checkout", "Checkout a copy of a module"),
  2079.             ("unlock", "Unlock a module"),
  2080.             ("lock", "Lock a module"),
  2081.             ("update", "Update a copy of a module"),
  2082.             ("done", "User is done with a module"),
  2083.             ("commit", "Commit changes to a module"),
  2084.             ("who", "Print a list of users with copies of a module"),
  2085.             ("diff", "Do rcsdiff on files you have changed"),
  2086.             ("status", "Print out rcs status of files"),
  2087.             ("log", "Print rcs log of files"),
  2088.             ("join", "Merge in new vendor release"),
  2089.             ("patch", "Create a patch file"),
  2090.             ("tag", "Tag a version"));
  2091.  
  2092.     &Opt_PrintUsage(@options);
  2093.     printf("\nValid commands are:\n");
  2094.     foreach $i sort ("unpack", "checkout", "unlock", "lock", "update", 
  2095.             "done", "commit", "who", "diff", "status", "log",
  2096.             @cvsCmds) {
  2097.     printf("\t$i\t%s\n", $info{$i});
  2098.     }
  2099. }
  2100.  
  2101. #
  2102. # Error($status, @args)
  2103. #
  2104. # Prints @args to STDERR, and returns $status
  2105. #
  2106. # Results: $status
  2107. #
  2108. # Side effects: Stuff is printed
  2109. #
  2110. sub Error {
  2111.     local($status) = shift;
  2112.     if ($#_ >= $[) {
  2113.     printf(STDERR @_);
  2114.     }
  2115.     return $status;
  2116. }
  2117.  
  2118. #
  2119. # ReadFile($file, $ignoreComments)
  2120. #
  2121. # Reads the contents of the given file.  If $ignoreComments is non-zero
  2122. # then any line beginning with '#' is ignored.  
  2123. #
  2124. # Results: An array containing each line of the file.  If a scalar is
  2125. #     wanted then only the first line is returned.
  2126. #
  2127. # Side effects: 
  2128. #
  2129. sub ReadFile {
  2130.     local($file) = shift;
  2131.     local($ignoreComments) = shift; 
  2132.     local(@contents);
  2133.     open(READ, "$file") ||
  2134.     return &Error(undef, "Open of $file failed: $!\n");
  2135.     if ($ignoreComments) {
  2136.     @contents = grep(!/^#/, <READ>);
  2137.     } else {
  2138.     @contents = <READ>;
  2139.     }
  2140.     close(READ);
  2141.     if ($#contents < $[) {
  2142.     return undef;
  2143.     }
  2144.     if (wantarray) {
  2145.     return @contents;
  2146.     } 
  2147.     return($contents[0]);
  2148. }
  2149.  
  2150. #
  2151. # WriteFile($file, @args)
  2152. #
  2153. # Writes @args to $file.  The file is created if it doesn't exist.
  2154. #
  2155. # Results: 0 if successful, 1 otherwise
  2156. #
  2157. # Side effects:  $file may be created, and it is written.
  2158. #
  2159. sub WriteFile {
  2160.     local($file) = shift;
  2161.     open(WRITE, ">$file") ||
  2162.     return &Error(1, "Open of $file failed: $!\n");
  2163.     print WRITE @_;
  2164.     close(WRITE);
  2165.     return 0;
  2166. }
  2167.  
  2168.  
  2169. #
  2170. # GetModuleName
  2171. #
  2172. # Gets the module name associated with a directory.
  2173. # If no directory is specified then the current working directory is used.
  2174. #
  2175. # Results: The module name.
  2176. #
  2177. # Side effects:  The cwdToModule array is filled in.
  2178. #
  2179. sub GetModuleName {
  2180.     local($dir) = shift;
  2181.     local($reposDir);
  2182.     local($index);
  2183.     local(@path);
  2184.     local($result) = undef; 
  2185.     local($found) = 0;
  2186.     local($i);
  2187.     local($owd) = $ENV{'PWD'};
  2188.     local($cwd);
  2189.     local($name);
  2190.  
  2191.     if (defined($dir)) {
  2192.     &Chdir($dir) == 0 || return undef;
  2193.     }
  2194.     $cwd = $ENV{'PWD'};
  2195.     $name = $cwdToModule{$cwd};
  2196.     if (!defined($result)) {
  2197.     if (!defined(%reposToModule)) {
  2198.         &ModMap;
  2199.     }
  2200.     $reposDir = &ReadFile("CVS.adm/Repository");
  2201.     chop($reposDir);
  2202.     printf("$reposDir\n") if ($debug);
  2203.     if (defined($reposDir)) {
  2204.         while($reposDir ne "") {
  2205.         $name = $reposToModule{$reposDir};
  2206.         if (defined($name)) {
  2207.             printf("Module $name\n") if ($debug);
  2208.             $result = $name;
  2209.             last;
  2210.         }
  2211.         $index = rindex($reposDir, '/');
  2212.         last if ($index < $[);
  2213.         $dir = substr($reposDir, 0, $index);
  2214.         }
  2215.     }
  2216.     }
  2217.     if (defined($result)) {
  2218.     $cwdToModule{$cwd} = $name;
  2219.     }
  2220.     if (defined($dir)) {
  2221.     &Chdir($owd) == 0 || return undef;
  2222.     }
  2223.     return $result;
  2224. }
  2225.  
  2226. #
  2227. # GetRootDir
  2228. #
  2229. # Given the name of a directory within a module copy returns the name
  2230. # of the root directory for that copy.  If a name is not given then
  2231. # the current directory is used.
  2232. #
  2233. # Results: The module name, undef otherwise.
  2234. #
  2235. # Side effects:  The cwdToRoot array is filled in.
  2236. #
  2237.  
  2238. sub GetRootDir {
  2239.     local($dir) = shift;
  2240.     local($owd) = $ENV{'PWD'};
  2241.     local($result) = undef;
  2242.  
  2243.     if (defined($dir)) {
  2244.     &Chdir($dir) == 0 || return undef;
  2245.     }
  2246.     $cwd = $ENV{'PWD'};
  2247.     $result = $cwdToRoot{$cwd};
  2248.     if (!defined($result)) {
  2249.  
  2250.  
  2251.  
  2252.  
  2253. #
  2254. # Chdir($dir)
  2255. #
  2256. # Changes the current working directory to $dir.  If the command fails
  2257. # an error message is printed. 
  2258. #
  2259. # Results: 0 if successful, 1 otherwise
  2260. #
  2261. # Side effects:  The current working directory is changed, and $ENV{'PWD'}
  2262. #     set to the new working directory.
  2263. #
  2264. sub Chdir {
  2265.     if (!&chdir($_[0])) {
  2266.     ($package, $file, $line) = caller;
  2267.     return &Error(1, "Chdir to %s from %s failed: $!\nFile %s Line %s", 
  2268.         $_[0], $ENV{'PWD'}, $file,$line);
  2269.     }
  2270.     return 0;
  2271. }
  2272.  
  2273. #
  2274. # ModMap
  2275. #
  2276. # Creates a mapping of module name to its subdirectory in the repository,
  2277. # and a mapping from the subdirectory to the module name.
  2278. #
  2279. # Results: 0 if successful, 1 otherwise
  2280. #
  2281. # Side effects:  The %moduleToRepos and %reposToModule are filled in.
  2282. #
  2283.  
  2284. sub ModMap {
  2285.     local($module, $dir);
  2286.     open(MOD, "cvs -d $cvsroot co -c |") ||
  2287.     return &Error(1, "Can't do \"cvs co -c\"\n");
  2288.     undef %moduleToRepos;
  2289.     while(<MOD>) {
  2290.     if (/^(\S+)\s+(\S+)/) {
  2291.         $moduleToRepos{$1} = $2;
  2292.         $reposToModule{$2} = $1;
  2293.     }
  2294.     }
  2295.     close(MOD);
  2296. }
  2297.  
  2298. #
  2299. # Main
  2300. #
  2301. #
  2302. $SIG{'INT'} = Exit;
  2303. &initpwd;
  2304. $tmpfile = "#SCVS.$$";
  2305. $status = 0;
  2306. if (&Config) {
  2307.     exit(1);
  2308. }
  2309. $command = shift;
  2310. if (!defined($command)) {
  2311.     &Usage(@options);
  2312.     exit(1);
  2313. }
  2314. printf("$command: %s\n", join(' ', @ARGV)) if ($debug);
  2315.  
  2316. if (($command eq "pack") || ($command eq "unpack")) {
  2317.     local(@options) = ("l", $OPT_FALSE, *recurse, "Recurse on subdirectories");
  2318.     &Opt_Parse(*ARGV, @options, 0);
  2319.     $status = &PackCmd($command, @ARGV);
  2320. } elsif (($command eq "checkout") || ($command eq "co")) {
  2321.     $command = "checkout";
  2322.     $status = &Checkout(@ARGV);
  2323. } elsif ($command eq "unlock") {
  2324.     $status = &UnlockCmd(@ARGV);
  2325. } elsif ($command eq "lock") {
  2326.     $status = &LockCmd(@ARGV);
  2327.     undef(@locks);
  2328. } elsif ($command eq "update") {
  2329.     $status = &UpdateCmd(1, @ARGV);
  2330. } elsif ($command eq "done") {
  2331.     $status = &DoneCmd(@ARGV);
  2332. } elsif (($command eq "commit") || ($command eq "ci")) {
  2333.     $status = &CommitCmd(@ARGV);
  2334. } elsif ($command eq "who") {
  2335.     $status = &WhoCmd(@ARGV);
  2336. } elsif ($command eq "add") {
  2337.     $status = &AddCmd(@ARGV);
  2338. } elsif ($command eq "remove") {
  2339.     $status = &RemoveCmd(@ARGV);
  2340. } elsif ($command eq "info") {
  2341.     $status = &InfoCmd(@ARGV);
  2342. } elsif ($command eq "diff") {
  2343.     $status = &DiffCmd(@ARGV);
  2344. } elsif (($command eq "status") || ($command eq "log")) {
  2345.     $status = &CvsCmd($command, @ARGV);
  2346. } elsif (grep($command eq $_, @cvsCmds)) {
  2347.     system("cvs -d $cvsroot $cvsCmdArgs $command @ARGV");
  2348.     $status = 0;
  2349. } else {
  2350.     printf("Bad command: $command\n");
  2351.     &Usage(@options);
  2352.     exit(1);
  2353. }
  2354.  
  2355. # Unlock any modules we may have locked.
  2356.  
  2357. if ($#locks >= $[) {
  2358.     &Unlock(0, @locks);
  2359. }
  2360. if ($status) {
  2361.     printf("$command failed\n");
  2362. }
  2363. exit($status);
  2364.